﻿// (c) Norbert Huffschmid
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.

using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Windows.Forms;
using NProtocol;
using ShoutBox.Core;
using ShoutBox.Core.Resources;
using ShoutBox.Resources;

namespace ShoutBox
{
    /// <summary>
    /// System tray icon
    /// </summary>
    public class SystemTrayIcon : IDisposable
    {
        private const bool POPUP_ON_RECEIVED_SHOUT_DEFAULT = true;
        private const int BALLOON_TIP_TIMEOUT = 2000;


        /// <summary>
        /// Fires when the form visibility should be toggled.
        /// </summary>
        public event EventHandler ToggleVisibility;

        /// <summary>
        /// Fires when the exit menu item has been clicked.
        /// </summary>
        public event EventHandler ExitClicked;


        private ViewModel viewModel;
        private NotifyIcon notifyIcon;
        private MenuItem popupOnReceivedShoutMenuItem;
        private bool disposed = false;


        /// <summary>
        /// Gets the PopupOnReceivedShout menuitem setting
        /// </summary>
        public bool PopupOnReceivedShout
        {
            get { return this.popupOnReceivedShoutMenuItem.Checked; }
        }


        /// <summary>
        /// Constructor
        /// </summary>
        public SystemTrayIcon(ViewModel viewModel)
        {
            this.viewModel = viewModel;
            this.viewModel.PropertyChanged += new PropertyChangedEventHandler(wpfViewModel_PropertyChanged);
            this.viewModel.SessionMembers.CollectionChanged +=
                new NotifyCollectionChangedEventHandler(shoutBoxes_CollectionChanged);
            this.viewModel.CurrentShouters.CollectionChanged +=
                new NotifyCollectionChangedEventHandler(CurrentShouters_CollectionChanged);

            // create initial icon
            this.notifyIcon = new NotifyIcon();
            this.notifyIcon.Text = Strings.AppName;
            this.notifyIcon.Icon = Icons.ShoutBoxIdle;
            this.notifyIcon.MouseClick += new MouseEventHandler(notifyIcon_MouseClick);
            this.notifyIcon.Visible = true;

            // create context menu
            ContextMenu contextMenu = new ContextMenu();
            MenuItem toggleMenuItem = new MenuItem(Strings.Minimize, ToggleMenuItem_Click);
            contextMenu.MenuItems.Add(toggleMenuItem);
            contextMenu.MenuItems.Add("-");
            this.popupOnReceivedShoutMenuItem = new MenuItem(Strings.PopupOnReceivedShout);
            this.popupOnReceivedShoutMenuItem.Checked = POPUP_ON_RECEIVED_SHOUT_DEFAULT;
            this.popupOnReceivedShoutMenuItem.Click += new EventHandler(popupOnReceivedShoutMenuItem_Click);
            contextMenu.MenuItems.Add(this.popupOnReceivedShoutMenuItem);
            contextMenu.MenuItems.Add("-");
            MenuItem exitMenuItem = new MenuItem(Strings.Exit, ExitMenuItem_Click); // shortcut visualization requires special user setting in Windows 7!
            contextMenu.MenuItems.Add(exitMenuItem);
            this.notifyIcon.ContextMenu = contextMenu;

            // show current listener state
            ShowListenerState(this.viewModel.ShoutBoxListenerState);
        }


        /// <summary>
        /// Show information about hidden client window
        /// </summary>
        public void ShowHideInformation()
        {
            ShowBalloonTip("... " + Strings.isNowHiddenHere + ".");
        }

        /// <summary>
        /// Set the meaning of the first menu item
        /// </summary>
        /// <param name="menuItemText">Open or Close</param>
        public void SetToggleInfo(string menuItemText)
        {
            if (this.notifyIcon.ContextMenu != null)
                this.notifyIcon.ContextMenu.MenuItems[0].Text = menuItemText;
        }

        /// <summary>
        /// Release all resources
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }


        protected virtual void OnToggleVisibility()
        {
            if (this.ToggleVisibility != null)
                this.ToggleVisibility(this, EventArgs.Empty);
        }

        protected virtual void OnExitClicked()
        {
            if (this.ExitClicked != null)
                this.ExitClicked(this, EventArgs.Empty);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!disposed)
            {
                if (disposing)
                {
                    this.notifyIcon.Dispose();
                }
            }
            disposed = true;
        }

        private void notifyIcon_MouseClick(object sender, MouseEventArgs e)
        {
            // opening the context menu with right mouse button causes the click event to trigger too!
            if (e.Button == MouseButtons.Left)
                OnToggleVisibility();
        }

        private void ToggleMenuItem_Click(object sender, EventArgs e)
        {
            OnToggleVisibility();
        }

        private void ExitMenuItem_Click(object sender, EventArgs e)
        {
            OnExitClicked();
        }

        private void popupOnReceivedShoutMenuItem_Click(object sender, EventArgs e)
        {
            // toggle checked condition
            this.popupOnReceivedShoutMenuItem.Checked = !this.popupOnReceivedShoutMenuItem.Checked;
        }

        private void wpfViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName.Equals("ShoutBoxListenerState"))
            {
                ShowListenerState(this.viewModel.ShoutBoxListenerState);
            }
            else if (e.PropertyName.Equals("ShoutTarget"))
            {
                // show hint about shout target 
                if (this.viewModel.ShoutTarget != null)
                {
                    this.notifyIcon.Text = Strings.AppName + " (" + this.viewModel.ShoutTarget.Name + ")";
                }
                else
                {
                    this.notifyIcon.Text = Strings.AppName;
                }
            }
        }

        private void shoutBoxes_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            // notify user about change
            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                string addedMember = ((ISessionMember)e.NewItems[0]).Name;
                ShowBalloonTip(Strings.AnotherShoutBoxIsNowAvailable + ": " + addedMember);
            }
            else if (e.Action == NotifyCollectionChangedAction.Remove)
            {
                string removedMember = ((ISessionMember)e.OldItems[0]).Name;
                ShowBalloonTip(Strings.ShoutBoxHasGone + ": " + removedMember);
            }
        }

        private void CurrentShouters_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            // determine collection of session member names 
            var shouterNames = from s in this.viewModel.CurrentShouters select s.Name;

            string shoutersCsv = string.Join(", ", shouterNames);

            if (shoutersCsv.Length > 0)
                ShowBalloonTip(Strings.SpeakingIs + ": " + shoutersCsv);
        }

        private void ShowBalloonTip(string tipText)
        {
            this.notifyIcon.ShowBalloonTip(BALLOON_TIP_TIMEOUT, Strings.AppName, tipText, ToolTipIcon.Info);
        }

        private void ShowListenerState(ListenerState listenerState)
        {
            if (listenerState == ListenerState.Active)
                this.notifyIcon.Icon = Icons.ShoutBoxActive;
            else if (listenerState == ListenerState.Waiting)
                this.notifyIcon.Icon = Icons.ShoutBoxWaiting;
            else
                this.notifyIcon.Icon = Icons.ShoutBoxIdle;
        }
    }
}
